/* Copyright (C) 2021 Rain */

/* This file is part of XNIX. */

/* 
  XNIX is free software: you can redistribute it and/or modify 
  it under the terms of the GNU General Public License as published by 
  the Free Software Foundation, either version 3 of the License, or 
  (at your option) and later version. 
*/

/*
  XNIX is distributed in the hope that it will be useful, 
  but WITHOUT ANY WARRANTY; without even the implied warranty of 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
  GNU General Public License for more details. 
*/

/*
  You should have received a copy of the GNU General Public License 
   along with XNIX. If not, see <https://www.gnu.org/licenses/>.  
*/




#include <kernel/init.h>
#include <kernel/errno.h>
#include <kernel/types.h>

#include <kernel/console.h>
#include <lib/string.h>
#include <lib/fifo.h>

#include <kernel/mm.h>
#include <kernel/keyboard.h>
#include <kernel/int.h>

#include <arch/x86/int.h>
#include <arch/io.h>
#include <arch/sys.h>


struct fifo kbd_fifo;
__uint8_t ctrl = 0, shift = 0, alt, capslock = 0;

/* the 0xe0 */
__uint8_t ext = 0;


errno_t kbd_init(void) {
	__fifo_buf_t *key_buf = kmalloc(&pool, MAX_KEYS * sizeof(__fifo_buf_t));

	memset(key_buf, 0x00, MAX_KEYS * sizeof(__uint64_t));

	fifo_init(&kbd_fifo, key_buf, MAX_KEYS);

	return 0;
}

void do_keyboard(void) {
	__uint8_t code = inb(KBD_DATA);

	/* insert it to the fifo */
	fifo_put(&kbd_fifo, code);

	return;
}


/* only displayable numbers/alphabet */
static char keycode_alphabet[] = {
	/* nul, esc */
	0, 0, '1', '2', '3', '4', '5', '6', 
	/* backspace, tab */
	'7', '8', '9', '0', '-', '=', 0, 0, 
	'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 
	/* enter, lctrl */
	'o', 'p', '[', ']', 0, 0, 'a', 's', 
	'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 
	/* lshift */
	'\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 
	/* rshift */
	'b', 'n', 'm', ',', '.', '/', 0, '*', 
	/* capslock, f1 - f5 */
	0, ' ', 0, 0, 0, 0, 0, 0, 
	/* f6 - f10, numlock, scrlock */
	0, 0, 0, 0, 0, 0, 0, '7', 
	'8', '9', '-', '4', '5', '6', '+', '1', 
	'2', '3', '0', '.'
};

static char keycode_alphabet_shift[] = {
	0, 0, '!', '@', '#', '$', '%', '^', 
	'&', '*', '(', ')', '_', '+', 0, 0, 
	'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 
	'O', 'P', '{', '}', 0, 0, 'A', 'S', 
	'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', 
	'"', '~', 0, '|', 'Z', 'X', 'C', 'V', 
	'B', 'N', 'M', '<', '>', '?', 0, '*', 
	0, ' ', 0, 0, 0, 0, 0, 0, 
	0, 0, 0, 0, 0, 0, 0, '7', 
	'8', '9', '-', '4', '5', '6', '+', '1', 
	'2', '3', '0', '.'
};

void put_keycode(void) {
	__fifo_buf_t i;
	i = fifo_get(&kbd_fifo);

	if (!i) 
		while (!kbd_fifo.need_read)
			hlt();
	

	if (i == EXT) {
		ext = 1;
		return;
	}


	/* the limit of keycode_alphabet */
	if (!ext && i < 0x54) {
		char a;

		if (!shift && !capslock) {
			if ((a = keycode_alphabet[i])) 
				putc(a, white);

			else 
				goto undisplayable;
		}

		else {
			if ((a = keycode_alphabet_shift[i]))
				putc(a, white);

			else 
				goto undisplayable;
		}

		/* its displayable, so return */

		return;
	}

undisplayable:
	if (i == EXT) {
		ext = 1;
		return;
	}

	if (i == CTRL) {
		if (ext) 
			/* got next character */
			ext = 0;
		
		ctrl = 1;
		return;
	}

	if (i == BREAK + CTRL) {
		if (ext) 
			ext = 0;

		ctrl = 0;
		return;
	}

	if (i == LSHIFT || i == RSHIFT) {
		shift = 1;
		return;
	}

	if (i == BREAK + LSHIFT || i == BREAK + RSHIFT) {
		shift = 0;
		return;
	}

	if (i == ALT) {
		if (ext) 
			ext = 0;

		alt = 1;
		return;
	}

	if (i == BREAK + ALT) {
		if (ext) 
			ext = 0;

		alt = 0;
		return;
	}

	if (i == CAPSLK) {
		capslock = !capslock;
		return;
	}

	if (i == BREAK + CAPSLK) 
		return;

	if (i == BACKSP) {
		putc('\b', white);
		return;
	}

	if (i == ENTER) {
		putc('\n', white);
		return;
	}

	if (i == TAB) {
		putc('\t', white);
		return;
	}

	ext = 0;

	return;
}
